﻿using Fluid;
using Fluid.Ast;
using Fluid.Filters;
using Fluid.Values;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace ZYing.LiquidThemeEngine.Tags
{
    public static class DataTags
    {
        public static async ValueTask<Completion> VarDumpAsync(TextWriter writer, TextEncoder encoder, TemplateContext ctx)
        {
            var context = (LiquidThemeTemplateContext)ctx;
            var data = new SortedDictionary<string, object>();
            var scope = context.GetLocalScope();
            while (scope != null)
            {
                var names = scope.Properties.ToArray();
                foreach (var name in names)
                {
                    if (data.ContainsKey(name))
                    {
                        continue;
                    }
                    data[name] = scope.GetValue(name);
                }
                scope = scope.ReleaseScope();
            }

            if (context.Model != null)
            {
                var type = context.Model.GetType();
                var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
                foreach (var p in properties)
                {
                    if (!p.CanRead)
                    {
                        continue;
                    }
                    var name = context.Options.MemberAccessStrategy.MemberNameStrategy(p);
                    if (data.ContainsKey(name)) continue;
                    var accessor = context.Options.MemberAccessStrategy.GetAccessor(type, name);
                    if (accessor == null)
                    {
                        if (context.AllowModelMembers)
                        {
                            var value = p.GetValue(context.Model);
                            if (value is Task task)
                            {
                                await task;
                                var taskType = task.GetType();
                                if (taskType.IsGenericType)
                                {
                                    dynamic dyn = task;
                                    data[name] = dyn.Result;
                                }
                                else
                                {
                                    data[name] = task;
                                }
                            }
                            else
                            {
                                data[name] = value;
                            }
                        }
                        continue;
                    }
                    if (accessor is IAsyncMemberAccessor asyncMemberAccessor)
                    {
                        data[name] = await asyncMemberAccessor.GetAsync(context.Model, name, context);
                    }
                    else
                    {
                        data[name] = accessor.Get(context.Model, name, context);
                    }
                }
            }
            var json = await MiscFilters.Json(
                FluidValue.Create(data, context.Options),
                new FilterArguments(BooleanValue.True),
                context
            );
            await writer.WriteAsync(json.ToStringValue());
            return Completion.Normal;
        }
    }
}
